//------------------------------------------------------------------------------------------------------
//   _____     _______ ____  _     ___  ____  ____    _____ _   _  ____ ___ _   _ _____   ______  ___ _ 
//  / _ \ \   / / ____|  _ \| |   / _ \|  _ \|  _ \  | ____| \ | |/ ___|_ _| \ | | ____| |  _ \ \/ / / |
// | | | \ \ / /|  _| | |_) | |  | | | | |_) | | | | |  _| |  \| | |  _ | ||  \| |  _|   | | | \  /| | |
// | |_| |\ V / | |___|  _ <| |__| |_| |  _ <| |_| | | |___| |\  | |_| || || |\  | |___  | |_| /  \| | |
//  \___/  \_/  |_____|_| \_\_____\___/|_| \_\____/  |_____|_| \_|\____|___|_| \_|_____| |____/_/\_\_|_|
//
// Overlord Engine v1.113
// Copyright Overlord Thomas Goussaert & Overlord Brecht Kets
// http://www.digitalartsandentertainment.com/
//------------------------------------------------------------------------------------------------------
//Precompiled Header [ALWAYS ON TOP IN CPP]
#include "Base\stdafx.h"

#include "ClothTestScene.h"
#include "Scenegraph\GameObject.h"
#include "Diagnostics\Logger.h"
#include "Diagnostics\DebugRenderer.h"

#include "Components/RigidBodyComponent.h"
#include "Components/ColliderComponent.h"
#include "Components/MeshDrawComponent.h"

#include "Content\ContentManager.h"
#include "Graphics\TextureData.h"
#include "Components\TransformComponent.h"
#include "Components\ModelComponent.h"
#include "Graphics\ModelAnimator.h"

#include "Prefabs/SpherePrefab.h"

#include "..\..\Materials\SkinnedDiffuseMaterial.h"

ClothTestScene::ClothTestScene(void) :
	GameScene(L"ClothTestScene"),
	m_Cloth(nullptr)
{
}


ClothTestScene::~ClothTestScene(void)
{
}

void ClothTestScene::Initialize(const GameContext& gameContext)
{
	UNREFERENCED_PARAMETER(gameContext);

	auto physX = PhysxManager::GetInstance()->GetPhysics();
	GetPhysxProxy()->EnablePhysxDebugRendering(true);

	auto bouncyMaterial = physX->createMaterial(0, 0, 1);
	auto ground = new GameObject();
	ground->AddComponent(new RigidBodyComponent(true));

	std::shared_ptr<PxGeometry> geom(new PxPlaneGeometry());
	ground->AddComponent(new ColliderComponent(geom, *bouncyMaterial, PxTransform(PxQuat(XM_PIDIV2, PxVec3(0, 0, 1)))));
	AddChild(ground);

	auto skinnedDiffuseMaterial = new SkinnedDiffuseMaterial();
	skinnedDiffuseMaterial->SetDiffuseTexture(L"./Resources/Textures/Knight.jpg");
	gameContext.pMaterialManager->AddMaterial(skinnedDiffuseMaterial, 0);

	// ADD SPHERE
	auto m_pSphere = new SpherePrefab();
	m_pSphere->GetTransform()->Translate(0,2,0);

	// Sphere PhysX
	auto rigidbody = new RigidBodyComponent();
	m_pSphere->AddComponent(rigidbody);

	std::shared_ptr<PxGeometry> spheregeom(new PxSphereGeometry(1));
	m_pSphere->AddComponent(new ColliderComponent(spheregeom,*bouncyMaterial, PxTransform(PxQuat(XM_PIDIV2,PxVec3(0,0,1)))));

	AddChild(m_pSphere);

	//Input
	gameContext.pInput->AddInputAction(InputAction(0, Released, 'R'));
	gameContext.pInput->AddInputAction(InputAction(1, Released, 'P'));
	gameContext.pInput->AddInputAction(InputAction(2, Released, 'O'));

	gameContext.pInput->AddInputAction(InputAction(3, Down, VK_UP));
	gameContext.pInput->AddInputAction(InputAction(4, Down, VK_DOWN));

	CreateCloth();

	PxClothCollisionPlane p;
	p.normal = PxVec3(0.0f, 1.0f, 0.0f);
	p.distance = 0.0f;
	
	PxU32 convexMask = 1; // Convex references the first plane only
	
	m_Cloth->addCollisionPlane(p);
	m_Cloth->addCollisionConvex(convexMask);

	PxClothCollisionSphere s;
	s.pos = PxVec3(0.0f, 1.0f, 0.0f);
	s.radius = 1.0f;

	m_Cloth->addCollisionSphere(s);

	m_Cloth->setActorFlag(PxActorFlag::eVISUALIZATION, true);
	m_Cloth->setClothFlag(PxClothFlag::eGPU, true);
}

void ClothTestScene::Update(const GameContext& gameContext)
{
		UNREFERENCED_PARAMETER(gameContext);
}

void ClothTestScene::Draw(const GameContext& gameContext)
{
	UNREFERENCED_PARAMETER(gameContext);
}

void ClothTestScene::CreateCloth()
{
	auto physX = PhysxManager::GetInstance()->GetPhysics();
	auto scene = this->GetPhysxProxy()->GetPhysxScene();

	PxTransform gPose = PxTransform(PxVec3(0,3,0));
	
	// create regular mesh
	PxU32 resolution = 20;
	PxU32 numParticles = resolution*resolution;
	PxU32 numTriangles = 2*(resolution-1)*(resolution-1);

	// create cloth particles
	PxClothParticle* particles = new PxClothParticle[numParticles];
	PxVec3 center(0.5f, 0.3f, 0.0f);
	PxVec3 delta = 1.0f/(resolution-1) * PxVec3(15.0f, 15.0f, 15.0f);
	PxClothParticle* pIt = particles;
	for(PxU32 i=0; i<resolution; ++i)
	{
		for(PxU32 j=0; j<resolution; ++j, ++pIt)
		{
			pIt->invWeight = j+1<resolution ? 1.0f : 0.0f;
			pIt->pos = delta.multiply(PxVec3(PxReal(i), 
				PxReal(j), -PxReal(j))) - center;
		}
	}

	// create triangles
	PxU32* triangles = new PxU32[3*numTriangles];
	PxU32* iIt = triangles;
	for(PxU32 i=0; i<resolution-1; ++i)
	{
		for(PxU32 j=0; j<resolution-1; ++j)
		{
			PxU32 odd = j&1u, even = 1-odd;
			*iIt++ = i*resolution + (j+odd);
			*iIt++ = (i+odd)*resolution + (j+1);
			*iIt++ = (i+1)*resolution + (j+even);
			*iIt++ = (i+1)*resolution + (j+even);
			*iIt++ = (i+even)*resolution + j;
			*iIt++ = i*resolution + (j+odd);
		}
	}

	// create fabric from mesh
	PxClothMeshDesc meshDesc;
	meshDesc.points.count = numParticles;
	meshDesc.points.stride = sizeof(PxClothParticle);
	meshDesc.points.data = particles;

	meshDesc.invMasses.count = numParticles;
	meshDesc.invMasses.stride = sizeof(PxClothParticle);
	meshDesc.invMasses.data = &particles->invWeight;
	
	meshDesc.triangles.count = numTriangles;
	meshDesc.triangles.stride = 3*sizeof(PxU32);
	meshDesc.triangles.data = triangles;


	// cook fabric
	PxClothFabric* fabric = PxClothFabricCreate(*physX, meshDesc, PxVec3(0, 1, 0));

	delete[] triangles;

	// create cloth
	m_Cloth = physX->createCloth(gPose, *fabric, particles, PxClothFlags(0));

	fabric->release();
	delete[] particles;

	// 240 iterations per/second (4 per-60hz frame)
	m_Cloth->setSolverFrequency(240.0f);

	scene->addActor(*m_Cloth);
}

//void ClothTestScene::CreateCloth()
//{
//	//Filling in PxClothMeshDesc
//		PxClothParticle vertices[] = {
//	    PxClothParticle(PxVec3(0.0f, 0.0f, 0.0f), 0.0f),
//	    PxClothParticle(PxVec3(0.0f, 1.0f, 0.0f), 1.0f),
//	    PxClothParticle(PxVec3(1.0f, 0.0f, 0.0f), 1.0f),
//	    PxClothParticle(PxVec3(1.0f, 1.0f, 0.0f), 1.0f)
//	};
//	
//	PxU32 primitives[] = { 0, 1, 3, 2 };
//	
//	PxClothMeshDesc meshDesc;
//	meshDesc.points.data = vertices;
//	meshDesc.points.count = 4;
//	meshDesc.points.stride = sizeof(PxClothParticle);
//	
//	meshDesc.invMasses.data = &vertices->invWeight;
//	meshDesc.invMasses.count = 4;
//	meshDesc.invMasses.stride = sizeof(PxClothParticle);
//	
//	meshDesc.quads.data = primitives;
//	meshDesc.quads.count = 1;
//	meshDesc.quads.stride = sizeof(PxU32) * 4;
//
//	//Creating Fabric
//	auto physX = PhysxManager::GetInstance()->GetPhysics();
//	PxClothFabric* fabric = PxClothFabricCreate(*physX, meshDesc, PxVec3(0, -1, 0));
//
//	auto obj = m_pModel->GetGameObject();
//	auto scene = obj->GetScene()->GetPhysxProxy()->GetPhysxScene();
//
//	//Creating Cloth
//	PxTransform pose = PxTransform(10, 10, 10);
//	PxCloth* cloth = physX->createCloth(pose, *fabric, vertices, PxClothFlags());
//	scene->addActor(*cloth);
//
//	//particle integration
//	cloth->setSolverFrequency(240.0f);
//
//	//Distance Constraints
//	cloth->setStretchConfig(PxClothFabricPhaseType::eVERTICAL, PxClothStretchConfig(1.0f));
//	cloth->setStretchConfig(PxClothFabricPhaseType::eHORIZONTAL, PxClothStretchConfig(0.9f));
//	cloth->setStretchConfig(PxClothFabricPhaseType::eSHEARING, PxClothStretchConfig(0.75f));
//	cloth->setStretchConfig(PxClothFabricPhaseType::eBENDING, PxClothStretchConfig(0.5f));
//
//	PxClothStretchConfig stretchConfig;
//	stretchConfig.stiffness = 0.8f;
//	stretchConfig.stiffnessMultiplier = 0.5f;
//	stretchConfig.compressionLimit = 0.6f;
//	stretchConfig.stretchLimit = 1.2f;
//	cloth->setStretchConfig(PxClothFabricPhaseType::eVERTICAL, stretchConfig);
//
//	// Two spheres located on the x-axis
//	PxClothCollisionSphere spheres[2] =
//	{
//	    PxClothCollisionSphere( PxVec3(-1.0f, 0.0f, 0.0f), 0.5f  ),
//	    PxClothCollisionSphere( PxVec3( 1.0f, 0.0f, 0.0f), 0.25f )
//	};
//	
//	cloth->setCollisionSpheres(spheres, 2);
//	cloth->addCollisionCapsule(0, 1);
//
//	cloth->setGlobalPose(PxTransform(PxVec3(1.0f, 0.0f, 0.0f)));
//	cloth->setTargetPose(PxTransform(PxVec3(1.0f, 0.0f, 0.0f)));
//	cloth->setInertiaScale(0.5f);
//
//	PxClothParticleMotionConstraint motionConstraints[] = {
//    PxClothParticleMotionConstraint(PxVec3(0.0f, 0.0f, 0.0f), 0.0f),
//    PxClothParticleMotionConstraint(PxVec3(0.0f, 1.0f, 0.0f), 1.0f),
//    PxClothParticleMotionConstraint(PxVec3(1.0f, 0.0f, 0.0f), 1.0f),
//    PxClothParticleMotionConstraint(PxVec3(1.0f, 1.0f, 0.0f), FLT_MAX)
//	};
//
//	cloth->setMotionConstraints(motionConstraints);
//
//	// Enable continuous collision detection
//	cloth->setClothFlag(PxClothFlag::eSWEPT_CONTACT, true);
//
//	//Virtual Particles
//	static PxVec3 weights[] =
//	{
//	    PxVec3(1.0f / 3, 1.0f / 3, 1.0f / 3), // center point
//	    PxVec3(4.0f / 6, 1.0f / 6, 1.0f / 6), // off-center point
//	};
//
//	PxU32 numFaces = meshDesc.triangles.count;
//
//	PxU8* triangles = (PxU8*)meshDesc.triangles.data;
//	
//	PxU32 *indices = new PxU32[numFaces*4*4];
//
//	for (PxU32 i = 0, *it = indices; i < numFaces; i++)
//	{
//	    PxU16* triangle = (PxU16*)triangles;
//	    PxU32 v0 = triangle[0];
//	    PxU32 v1 = triangle[1];
//	    PxU32 v2 = triangle[2];
//	
//	    // center
//	    *it++ = v0; *it++ = v1; *it++ = v2; *it++ = 0;
//	
//	    // off centers
//	    *it++ = v0; *it++ = v1; *it++ = v2; *it++ = 1;
//	    *it++ = v1; *it++ = v2; *it++ = v0; *it++ = 1;
//	    *it++ = v2; *it++ = v0; *it++ = v1; *it++ = 1;
//	
//	    triangles += meshDesc.triangles.stride;
//	}
//	
//	cloth->setVirtualParticles(numFaces*4, indices, 2, weights);
//	delete[] indices;
//
//	cloth->setFrictionCoefficient(0.5f);
//	cloth->setCollisionMassScale(20.0f);
//	cloth->setSelfCollisionDistance(0.1f);
//	cloth->setSelfCollisionStiffness(1.0f);
//
//	scene->setClothInterCollisionDistance(0.5f);
//	scene->setClothInterCollisionStiffness(1.0f);
//	cloth->setClothFlag(PxClothFlag::eGPU, true);
//}
